#coding:utf-8
import os
import sys
from kivy.config import Config

if sys.platform.startswith('linux') or sys.platform.startswith('win32') or sys.platform.startswith('cygwin') or sys.platform.startswith('darwin'):
    Config.set('graphics', 'width', 960)
    Config.set('graphics', 'height', 540)
else:
    Config.set('graphics', 'width', 480)
    Config.set('graphics', 'height', 960)

import time
import json
import glob
from copy import deepcopy
import kivy
import pandas as pd

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

sys.path.append(os.path.pardir)
from Data.Draw_List import DRAW_LIST
from Calculate.Calculate import Numerical, Analytic
from Plot.Plot import Plot

WORK_SPACE = os.path.abspath(os.path.join(
    os.path.dirname(__file__), os.path.pardir))
DATASETS = glob.glob(os.path.join(WORK_SPACE, 'Data', '*.csv'))
SETTINGS = glob.glob(os.path.join(WORK_SPACE, 'Data', 'Net_Setting*.json'))

class Result(BoxLayout):
    pass

class Plot_Setting(BoxLayout):
    pass

class MainWindow(BoxLayout):
    pass

class NuclearApp(App):
    def __init__(self, **kwargs):
        super(NuclearApp, self).__init__(**kwargs)
        print(WORK_SPACE)
        self.kv_file = glob.glob(os.path.join(WORK_SPACE, 'Data', '*.kv'))[0]
        self.FONT = kivy.resources.resource_find(glob.glob(os.path.join(WORK_SPACE, 'Data', '*.ttf'))[0])
        self.DATA = {}
        for CSV_File in DATASETS:
            with open(CSV_File, encoding='gbk') as csv_file:
                self.DATA[CSV_File[-8:-4]] = pd.read_csv(csv_file).to_numpy()
        with open(SETTINGS[-1], 'r') as f:
            self.default_setting = json.load(f)
        self.Plot_Settings = {}
        self.draw_list = DRAW_LIST
        self.numerical_widget = None
        self.analytical_widget = None
        self.index = 0
    
    def build(self):
        
        if sys.platform.startswith('linux') or sys.platform.startswith('win32') or sys.platform.startswith('cygwin') or sys.platform.startswith('darwin'):
            self.root = MainWindow()
            self.root.ids.main_setting.size_hint_x = 3
            self.root.ids.results.size_hint_x = 4
        else:
            self.root = MainWindow(orientation='vertical')
            self.root.ids.main_setting.size_hint_y = 3
            self.root.ids.results.size_hint_y = 4

        self.root.ids.mode.active = True if self.default_setting['Method'] == 'Flux' else False
        self.root.ids.Flux_Power.text = '%e'%self.default_setting[self.default_setting['Method']]
        self.root.ids.Run_Time.text = '%d'%self.default_setting['t']
        self.root.ids.DT.value = '%e'%self.default_setting['DT']
        self.root.ids.Accuracy.text = '%e'%self.default_setting['Accuracy']

        for name, nuclides in self.default_setting['Initial'].items():
            self.Plot_Settings[name] = {}
            for nuclide, weight in nuclides.items():
                self.Plot_Settings[name][nuclide] = Plot_Setting()
                self.Plot_Settings[name][nuclide].ids.nuclide.text = nuclide
                self.Plot_Settings[name][nuclide].ids.Ratio.text = '%e'%DRAW_LIST[name][nuclide]['Ratio']
                self.Plot_Settings[name][nuclide].ids.Show.active = DRAW_LIST[name][nuclide]['Enable']
                self.root.ids.plot_settings.add_widget(self.Plot_Settings[name][nuclide])
                if nuclide == '234U':
                    self.root.ids.Weight_U_234.text = '%d'%weight
                elif nuclide == '235U':
                    self.root.ids.Weight_U_235.text = '%d'%weight
                    self.root.ids.Weight_U_235_.text = '%d'%weight
                elif nuclide == '238U':
                    self.root.ids.Weight_U_238.text = '%d'%weight
        return self.root

    def Get_Results(self):
        target_list = [
            self.root.ids.Flux_Power.text,
            self.root.ids.Run_Time.text,
            self.root.ids.Weight_U_235_.text
        ]
        type_list = [
            float,
            int,
            int
        ]
        error_message = self.Check_Target(
            [
                '中子通量（每平方厘米每秒）' if self.root.ids.mode.active else "比功率（兆瓦每吨）",
                "运行时间",
                "235U含量"
            ],
            target_list,
            type_list
        )
        if error_message == '':
            U235 = int(target_list[2])
            if U235 > 1000:
                self.Send_Error('235U初始含量大于100%%')
            else:
                self.Set_U235(U235)
                self.default_setting['Method'] = 'Flux' if self.root.ids.mode.active else 'Power'
                self.default_setting[self.default_setting['Method']] = type_list[0](target_list[0])
                self.default_setting['t'] = type_list[1](target_list[1])
                self.default_setting['DT'] = int(self.root.ids.DT.value)
                if self.analytical_widget is not None:
                    self.root.ids.results.remove_widget(self.analytical_widget)
                    self.analytical_widget = None
                if self.root.ids.analytical.active:
                    self.index += 1
                    self.analytical_widget = Result()
                    self.root.ids.results.add_widget(self.analytical_widget)
                    start = time.time()
                    analytic = Analytic(self.DATA, self.default_setting)
                    analytic.main(self.default_setting['t'] * 24 * 60 * 60, self.default_setting['Accuracy'])
                    stop = time.time()
                    plot = Plot(analytic.numerical_result)
                    plot.Draw(self.draw_list)
                    ANALYTICAL = os.path.join(os.path.dirname(__file__), 'Analytical_%s.png'%self.index)
                    plot.Save(ANALYTICAL)
                    self.analytical_widget.ids.image.source = ANALYTICAL
                    self.analytical_widget.ids.label.text = "解析方法计算用时%.3e秒。" % (stop-start)
                    os.remove(ANALYTICAL)
                if self.numerical_widget is not None:
                    self.root.ids.results.remove_widget(self.numerical_widget)
                    self.numerical_widget = None
                if self.root.ids.numerical.active:
                    self.index += 1
                    self.numerical_widget = Result()
                    self.root.ids.results.add_widget(self.numerical_widget)
                    start = time.time()
                    numerical = Numerical(self.DATA, self.default_setting)
                    numerical.main(self.default_setting['t'] * 24 * 60 * 60, self.default_setting['Accuracy'])
                    stop = time.time()
                    plot = Plot(numerical.numerical_result)
                    plot.Draw(self.draw_list)
                    NUMERICAL = os.path.join(os.path.dirname(__file__), 'Numerical_%s.png'%self.index)
                    plot.Save(NUMERICAL)
                    self.numerical_widget.ids.image.source = NUMERICAL
                    self.numerical_widget.ids.label.text = "解析方法计算用时%.3e秒。" % (stop-start)
                    os.remove(NUMERICAL)
        else:
            self.Send_Error(error_message)

    def Open_Basic(self):
        error_message = self.Check_Target(['235U含量'], [self.root.ids.Weight_U_235_.text], [int])
        if error_message == '':
            U235 = int(self.root.ids.Weight_U_235_.text)
            if U235 > 1000:
                self.Send_Error('235U初始含量大于100%%')
            else:
                self.Set_U235(U235)
                self.root.ids.popup_basic.open()
        else:
            self.Send_Error(error_message)

    def Set_Basic(self, IF_SAVE):
        target_list = [
            self.root.ids.Accuracy.text,
            self.root.ids.Weight_U_234.text,
            self.root.ids.Weight_U_235.text,
            self.root.ids.Weight_U_238.text
        ]
        type_list = [
            float,
            int,
            int,
            int
        ]
        if IF_SAVE:
            error_message = self.Check_Target(
                [
                    "内步长精度",
                    "234U含量",
                    "235U含量",
                    "238U含量"
                ],
                target_list,
                type_list
            )
            if error_message == '':
                total = 0
                for target in target_list[1:]:
                    total += int(target)
                if total > 1000:
                    self.Send_Error('铀初始含量之和大于100%%')
                elif float(target_list[0]) == 0:
                    self.Send_Error('内步长精度过小')
                else:
                    self.default_setting['Accuracy'] = type_list[0](target_list[0])
                    self.default_setting['Initial']['235U']['234U'] = type_list[1](target_list[1])
                    self.default_setting['Initial']['235U']['235U'] = type_list[2](target_list[2])
                    self.default_setting['Initial']['238U']['238U'] = type_list[3](target_list[3])
                    self.root.ids.popup_basic.dismiss()
            else:
                self.Send_Error(error_message)
        else:
            self.root.ids.Accuracy.text = '%e'%self.default_setting['Accuracy']
            self.root.ids.Weight_U_234.text = '%d'%self.default_setting['Initial']['235U']['234U']
            self.root.ids.Weight_U_235.text = '%d'%self.default_setting['Initial']['235U']['235U']
            self.root.ids.Weight_U_235_.text = '%d'%self.default_setting['Initial']['235U']['235U']
            self.root.ids.Weight_U_238.text = '%d'%self.default_setting['Initial']['238U']['238U']
            self.root.ids.popup_basic.dismiss()

    def Set_Plot(self, IF_SAVE):
        if IF_SAVE:
            error_message = ''
            for name, nuclides in self.Plot_Settings.items():
                name_list = []
                target_list = []
                type_list = []
                for nuclide, settings in nuclides.items():
                    name_list.append(nuclide)
                    target_list.append(settings.ids.Ratio.text)
                    type_list.append(float)
                error_message += self.Check_Target(name_list, target_list, type_list)
            if error_message == '':
                for name, nuclides in self.Plot_Settings.items():
                    for nuclide, settings in nuclides.items():
                        DRAW_LIST[name][nuclide]['Ratio'] = float(settings.ids.Ratio.text)
                        settings.ids.Ratio.text = '%e'%DRAW_LIST[name][nuclide]['Ratio']
                        DRAW_LIST[name][nuclide]['Enable'] = settings.ids.Show.active
                self.root.ids.popup_plot.dismiss()
            else:
                self.Send_Error(error_message)
        else:
            for name, nuclides in self.Plot_Settings.items():
                for nuclide, settings in nuclides.items():
                    settings.ids.Ratio.text = '%e'%DRAW_LIST[name][nuclide]['Ratio']
                    settings.ids.Show.active = DRAW_LIST[name][nuclide]['Enable']
            self.root.ids.popup_plot.dismiss()

    def Check_Target(self, name_list, target_list, type_list):
        error_message = ''
        for name, target, data_type in zip(name_list, target_list, type_list):
            try:
                target = data_type(target)
                if target < 0:
                    error_message += '“%s”小于零\n'%name
            except:
                error_message += '“%s”不符合目标格式\n'%name
        return error_message

    def Set_U235(self, U235):
        U234 = deepcopy(self.default_setting['Initial']['235U']['234U'])
        U238 = deepcopy(self.default_setting['Initial']['238U']['238U'])
        if U235 + U234 + U238 > 1000:
            self.default_setting['Initial']['235U']['234U'] = int((1000 - U235) * U234 / (U234 + U238))
            self.default_setting['Initial']['238U']['238U'] = int((1000 - U235) * U238 / (U234 + U238))
            self.root.ids.Weight_U_234.text = '%d'%self.default_setting['Initial']['235U']['234U']
            self.root.ids.Weight_U_238.text = '%d'%self.default_setting['Initial']['238U']['238U']
            self.default_setting['Initial']['235U']['235U'] = U235
            self.root.ids.Weight_U_235_.text = '%d'%self.default_setting['Initial']['235U']['235U']
            self.root.ids.Weight_U_235.text = '%d'%self.default_setting['Initial']['235U']['235U']

    def Send_Error(self, message):
        self.root.ids.error_message.text = message
        self.root.ids.popup_error.open()

    def Renew_Mode(self):
        try:
            self.default_setting[self.default_setting['Method']] = float(self.root.ids.Flux_Power.text)
        except:
            pass
        self.default_setting['Method'] = 'Flux' if self.root.ids.mode.active else 'Power'
        self.root.ids.Flux_Power.text = '%e'%self.default_setting[self.default_setting['Method']]

if __name__ == '__main__':
    myapp = NuclearApp()
    myapp.run()